library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity arithmetic_logic_unit is
generic (N : integer := 4);
port(	left		: in   std_logic_vector (N-1 downto 0);	-- left operand (A or NPC)
		right	: in   std_logic_vector (N-1 downto 0);	-- right operand (B or Imm)
		alu_mux	: in   std_logic_vector (2 downto 0);		-- operation to perform (activates the correct output)
		output	: out std_logic_vector (N-1 downto 0)	-- ALU output
);
end arithmetic_logic_unit;

architecture Behavioral of arithmetic_logic_unit is
component adder_evo is
generic (	N : integer := 16;
		M : integer := 4);
port(	A	: in   std_logic_vector (N-1 downto 0);
		B	: in   std_logic_vector (N-1 downto 0);
		C_in	: in   std_logic;
		SUB	: in   std_logic;
		SIGN: in   std_logic;
		OVF	: out std_logic;
		S 	: out std_logic_vector (N-1 downto 0)
);
end component;

component mult is
generic (N : integer := 32);
port( 	A	: in   std_logic_vector (N/2-1 downto 0);
		B	: in   std_logic_vector (N/2-1 downto 0);
		M	: out std_logic_vector (N-1 downto 0)
);
end component;

component logics is
generic (N : integer := 32);
port(	data_reg		: in   std_logic_vector (N-1 downto 0);
		immediate	: in   std_logic_vector (N-1 downto 0);
		op_type		: in   std_logic_vector (1 downto 0);
		data_out		: out std_logic_vector (N-1 downto 0)
);
end component;

signal adder_out 	: std_logic_vector (N-1 downto 0);
signal mult_out 	: std_logic_vector (N-1 downto 0);
signal logical_out	: std_logic_vector (N-1 downto 0);
begin

process (left,right,alu_mux,adder_out,mult_out,logical_out)
begin

	case alu_mux is
		when "001"|"010" => 			-- addition or subtraction
			output <= adder_out;
		when "011" => 				-- multiplication
			output <= mult_out;
		when "100"|"101"|"110" => 	-- logics
			output <= logical_out;
		when "111" =>				-- JUMP & LD/ST
			output <= right;
		when others =>
			output <= (others => '0');
	end case;

end process;



ADD_SUB: adder_evo generic map (4,2) port
map (	A	=> left,
		B	=> right,
		C_in	=> '0',
		SUB	=> alu_mux(1),
		SIGN=> '0',
		OVF	=> open,
		S 	=> adder_out
);

MUL		: mult generic map(N) port
map (	A 	=> left (N/2-1 downto 0),
		B 	=> right (N/2-1 downto 0),
		M	=> mult_out
);

LOG 	: logics generic map(N) port
map (	data_reg		=> left,
		immediate	=> right,
		op_type		=> alu_mux(1 downto 0),
		data_out		=> logical_out
	);

end Behavioral;
